Prerrequisitos

Se requiere tener instaladas y cargadas las siguientes paqueterías.

library(tidyverse)
library(tidyquant)
library(plotly)
library(tsibble)
library(readxl)
library(fpp3)

Algunas series de tiempo

Tipo de cambio USD/JPY

Cargamos los datos a R.

usd_jpy <- read_csv("USD_JPY.csv")
Missing column names filled in: 'X1' [1]Parsed with column specification:
cols(
  X1 = col_double(),
  TimeStamp = col_datetime(format = ""),
  Open = col_double(),
  High = col_double(),
  Low = col_double(),
  Close = col_double()
)
usd_jpy

Gráfica de tiempo

p <- usd_jpy %>% 
  ggplot(aes(x = TimeStamp, y = Close)) +
  geom_line()

ggplotly(p)

Gráfica de velas

p <- ggplot(data = usd_jpy, aes(x = TimeStamp, y = Close)) +
  geom_candlestick(aes(open = Open, high = High, low = Low, close = Close), colour_up = "darkgreen",colour_down = "red", size = 1)
p


p + coord_x_datetime(xlim = c("2019-07-01","2019-08-01"), 
                     ylim = c(106,109.5))

Horas que pasan los americanos durmiendo

Carga de los datos.

americans <- read_excel("Time Americans Spend Sleeping.xlsx")
americans

Gráfica por tipo de días

p <- americans %>% 
  filter(Sex == "Both") %>% 
  ggplot(aes(x = Year, `Avg hrs per day sleeping`, 
             color = `Age Group`)) +
  geom_line() + facet_wrap(~ `Type of Days`, nrow = 1)

ggplotly(p)

Gráfica por tipo de días y Sexo

p <- americans %>% 
  ggplot(aes(x = Year, `Avg hrs per day sleeping`, 
             color = `Age Group`)) +
  geom_line() + facet_grid(`Type of Days` ~ Sex)

ggplotly(p)

Ventas de autos

car_sales <- tq_get("TOTALNSA", get = "economic.data", from = "1977-01-01")
car_sales

Gráfica de tiempo

p <- car_sales %>% 
  ggplot(aes(x = date, y = price)) + 
  geom_line()

ggplotly(p)

Tipos de ajustes/transformaciones

Ajustes por población

Tomaremos la tabla global_economy y vamos a filtrarla para quedarnos con cuatro países y graficar el PIB y el PIB con escala logarítmica.

ge <- global_economy %>% 
  filter(Country %in% c("Mexico", "Iceland", "Australia", "Colombia"))

p3 <- ggplot(ge) + aes(x = Year, y = GDP, color = Country) + 
  geom_line(size = 1)

p3

Revisando la última población registrada de cada país.

ge %>% 
  filter(Year == 2017) %>% 
  arrange(desc(Population))

Vemos que existe una gran diferencia en la población de estos países. Para poder comparar el PIB entre ellos, sería mejor realizar un ajuste por población y revisar la variable PIB per cápita.

ge <- global_economy %>% 
  filter(Country %in% c("Australia", "Mexico", "Iceland", "Colombia"))

p4 <- ggplot(ge) + aes(x = Year, y = GDP / Population, color = Country) +
  geom_line(size = 1) + ylab("GDP per capita")

p4

Transformaciones matemáticas

Comúnmente se podrán llevar a cabo transformaciones logarítmicas.

p3 + scale_y_log10()

Ajustes por inflación

Tomaremos la tabla precargada aus_retail y seleccionaremos la industria de impresión. Resumiremos los datos de manera anual y compararemos las gráficas de la serie en precios corrientes y ajustados por inflación (o a precios constantes).

print_retail <- aus_retail %>%
  filter(Industry == "Newspaper and book retailing") %>%
  group_by(Industry) %>%
  index_by(Year = year(Month)) %>%
  summarise(Turnover = sum(Turnover))
aus_economy <- global_economy %>%
  filter(Code == "AUS")
print_retail %>%
  left_join(aus_economy, by = "Year") %>%
  mutate(Adjusted_turnover = Turnover / CPI) %>%
  gather("Type", "Turnover", Turnover, Adjusted_turnover, factor_key = TRUE) %>%
  ggplot(aes(x = Year, y = Turnover)) +
    geom_line() +
    facet_grid(vars(Type), scales = "free_y") +
    xlab("Years") + ylab(NULL) +
    ggtitle("Turnover for the Australian print media industry")

El ecosistema tidyverts

Estas paqueterías son parte del tidyverts y utilizan la misma filosofía del tidyverse, pero se especializan en el análisis de series de tiempo.

library(tsibble)
library(feasts)
library(fable)

Transformaciones de Box-Cox

Otra familia de transformaciones, que depende de un parámetro \(\lambda\) para escoger el tipo de transformación a realizar. Para ejemplificarlo, tomamos los datos precargados aus_production.

aus_production

Graficar la producción de gas.

aus_production %>% autoplot(Gas)

Queremos estabilizar la varianza y una transformación logarítmica (o Box-Cox con \(\lambda = 0\)) parece no ser suficiente:

aus_production %>% autoplot(log(Gas))
There were 20 warnings (use warnings() to see them)

Obtenemos la característica de Guerrero para decidir el valor óptimo de \(\lambda\).

(lambda <- aus_production %>%
  features(Gas, features = guerrero) %>%
  pull(lambda_guerrero))
[1] 0.1204864

Podemos aplicar la transformación directo en el autoplot.

aus_production %>% autoplot(box_cox(Gas, lambda))

Descomposición de series de tiempo

us_retail_employment <- us_employment %>%
  filter(year(Month) >= 1990, Title == "Retail Trade") %>%
  select(-Series_ID)

us_retail_employment

us_retail_employment %>%
  autoplot(Employed) +
  xlab("Year") + ylab("Persons (thousands)") +
  ggtitle("Total employment in US retail")

dcmp <- us_retail_employment %>%
  model(STL(Employed))

components(dcmp)
us_retail_employment %>%
  autoplot(Employed, color='gray') +
  autolayer(components(dcmp), trend, color='red') +
  xlab("Year") + ylab("Persons (thousands)") +
  ggtitle("Total employment in US retail")

components(dcmp) %>% autoplot() + xlab("Year")

us_retail_employment %>%
  autoplot(Employed, color='gray') +
  autolayer(components(dcmp), season_adjust, color='blue') +
  xlab("Year") + ylab("Persons (thousands)") +
  ggtitle("Total employment in US retail")

Descomposición clásica

us_retail_employment %>%
  model(classical_decomposition(Employed, type = "additive")) %>%
  components() %>%
  autoplot() + xlab("Year") +
  ggtitle("Classical additive decomposition of total US retail employment")

Descomposición X11

x11_dcmp <- us_retail_employment %>%
  model(x11 = feasts:::X11(Employed, type = "additive")) %>%
  components()

autoplot(x11_dcmp) + xlab("Year") +
  ggtitle("Additive X11 decomposition of US retail employment in the US")

x11_dcmp %>% 
  gg_season()
Plot variable not specified, automatically selected `y = Employed`

x11_dcmp %>% 
  gg_subseries(seasonal)

Descomposición SEATS

seats_dcmp <- us_retail_employment %>%
  model(seats = feasts:::SEATS(Employed)) %>%
  components()
autoplot(seats_dcmp) + xlab("Year") +
  ggtitle("SEATS decomposition of total US retail employment")

Descomposición STL

us_retail_employment %>%
  model(STL(Employed ~ trend(window=7) + season(window = "periodic"),
    robust = TRUE)) %>%
  components() %>%
  autoplot()

usd_ts <- usd_jpy %>%
  as_tsibble(index = TimeStamp) %>% 
  fill_gaps() %>% fill(Close)

usd_ts %>% 
  model(STL(Close)) %>% 
  components() %>% autoplot()

ge %>% 
  model(STL(log(GDP))) %>% 
  components() %>% 
  autoplot()

LS0tDQp0aXRsZTogIlNlcmllcyBkZSB0aWVtcG8geSBkZXNjb21wb3NpY2nDs24iDQpzdWJ0aXRsZTogIkNsYXNlIDAyIg0KYXV0aG9yOiAiUGFibG8gQmVuYXZpZGVzLUhlcnJlcmEiDQpkYXRlOiAyMDIwLTA2LTAzDQpvdXRwdXQ6IA0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogVFJVRQ0KICAgIHRvY19mbG9hdDogVFJVRQ0KICAgIHRoZW1lOiB1bml0ZWQNCiAgICBoaWdobGlnaHQ6IHRhbmdvDQotLS0NCg0KIyBQcmVycmVxdWlzaXRvcw0KDQpTZSByZXF1aWVyZSB0ZW5lciBpbnN0YWxhZGFzIHkgY2FyZ2FkYXMgbGFzIHNpZ3VpZW50ZXMgcGFxdWV0ZXLDrWFzLg0KDQpgYGB7ciBwa2dzLCBtZXNzYWdlPUZBTFNFfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHRpZHlxdWFudCkNCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeSh0c2liYmxlKQ0KbGlicmFyeShyZWFkeGwpDQpsaWJyYXJ5KGZwcDMpDQpgYGANCg0KIyBBbGd1bmFzIHNlcmllcyBkZSB0aWVtcG8NCg0KIyMgVGlwbyBkZSBjYW1iaW8gVVNEL0pQWQ0KDQpDYXJnYW1vcyBsb3MgZGF0b3MgYSAqKlIqKi4NCg0KYGBge3J9DQp1c2RfanB5IDwtIHJlYWRfY3N2KCJVU0RfSlBZLmNzdiIpDQp1c2RfanB5DQpgYGANCg0KIyMjIEdyw6FmaWNhIGRlIHRpZW1wbw0KDQpgYGB7cn0NCnAgPC0gdXNkX2pweSAlPiUgDQogIGdncGxvdChhZXMoeCA9IFRpbWVTdGFtcCwgeSA9IENsb3NlKSkgKw0KICBnZW9tX2xpbmUoKQ0KDQpnZ3Bsb3RseShwKQ0KYGBgDQoNCiMjIyBHcsOhZmljYSBkZSB2ZWxhcw0KDQpgYGB7cn0NCnAgPC0gZ2dwbG90KGRhdGEgPSB1c2RfanB5LCBhZXMoeCA9IFRpbWVTdGFtcCwgeSA9IENsb3NlKSkgKw0KICBnZW9tX2NhbmRsZXN0aWNrKGFlcyhvcGVuID0gT3BlbiwgaGlnaCA9IEhpZ2gsIGxvdyA9IExvdywgY2xvc2UgPSBDbG9zZSksIGNvbG91cl91cCA9ICJkYXJrZ3JlZW4iLGNvbG91cl9kb3duID0gInJlZCIsIHNpemUgPSAxKQ0KcA0KDQpwICsgY29vcmRfeF9kYXRldGltZSh4bGltID0gYygiMjAxOS0wNy0wMSIsIjIwMTktMDgtMDEiKSwgDQogICAgICAgICAgICAgICAgICAgICB5bGltID0gYygxMDYsMTA5LjUpKQ0KYGBgDQoNCiMjIEhvcmFzIHF1ZSBwYXNhbiBsb3MgYW1lcmljYW5vcyBkdXJtaWVuZG8NCg0KQ2FyZ2EgZGUgbG9zIGRhdG9zLg0KDQpgYGB7cn0NCmFtZXJpY2FucyA8LSByZWFkX2V4Y2VsKCJUaW1lIEFtZXJpY2FucyBTcGVuZCBTbGVlcGluZy54bHN4IikNCmFtZXJpY2Fucw0KYGBgDQoNCiMjIyBHcsOhZmljYSBwb3IgdGlwbyBkZSBkw61hcw0KDQpgYGB7cn0NCnAgPC0gYW1lcmljYW5zICU+JSANCiAgZmlsdGVyKFNleCA9PSAiQm90aCIpICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gWWVhciwgYEF2ZyBocnMgcGVyIGRheSBzbGVlcGluZ2AsIA0KICAgICAgICAgICAgIGNvbG9yID0gYEFnZSBHcm91cGApKSArDQogIGdlb21fbGluZSgpICsgZmFjZXRfd3JhcCh+IGBUeXBlIG9mIERheXNgLCBucm93ID0gMSkNCg0KZ2dwbG90bHkocCkNCmBgYA0KDQojIyMgR3LDoWZpY2EgcG9yIHRpcG8gZGUgZMOtYXMgeSBTZXhvDQoNCmBgYHtyfQ0KcCA8LSBhbWVyaWNhbnMgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBZZWFyLCBgQXZnIGhycyBwZXIgZGF5IHNsZWVwaW5nYCwgDQogICAgICAgICAgICAgY29sb3IgPSBgQWdlIEdyb3VwYCkpICsNCiAgZ2VvbV9saW5lKCkgKyBmYWNldF9ncmlkKGBUeXBlIG9mIERheXNgIH4gU2V4KQ0KDQpnZ3Bsb3RseShwKQ0KYGBgDQoNCg0KIyMgVmVudGFzIGRlIGF1dG9zDQoNCmBgYHtyfQ0KY2FyX3NhbGVzIDwtIHRxX2dldCgiVE9UQUxOU0EiLCBnZXQgPSAiZWNvbm9taWMuZGF0YSIsIGZyb20gPSAiMTk3Ny0wMS0wMSIpDQpjYXJfc2FsZXMNCmBgYA0KDQojIyMgR3LDoWZpY2EgZGUgdGllbXBvDQoNCmBgYHtyfQ0KcCA8LSBjYXJfc2FsZXMgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBkYXRlLCB5ID0gcHJpY2UpKSArIA0KICBnZW9tX2xpbmUoKQ0KDQpnZ3Bsb3RseShwKQ0KYGBgDQoNCiMgVGlwb3MgZGUgYWp1c3Rlcy90cmFuc2Zvcm1hY2lvbmVzDQoNCiMjIEFqdXN0ZXMgcG9yIHBvYmxhY2nDs24NCg0KVG9tYXJlbW9zIGxhIHRhYmxhIGBnbG9iYWxfZWNvbm9teWAgeSB2YW1vcyBhIGZpbHRyYXJsYSBwYXJhIHF1ZWRhcm5vcyBjb24gY3VhdHJvIHBhw61zZXMgeSBncmFmaWNhciBlbCBQSUIgeSBlbCBQSUIgY29uIGVzY2FsYSBsb2dhcsOtdG1pY2EuDQoNCmBgYHtyfQ0KZ2UgPC0gZ2xvYmFsX2Vjb25vbXkgJT4lIA0KICBmaWx0ZXIoQ291bnRyeSAlaW4lIGMoIk1leGljbyIsICJJY2VsYW5kIiwgIkF1c3RyYWxpYSIsICJDb2xvbWJpYSIpKQ0KDQpwMyA8LSBnZ3Bsb3QoZ2UpICsgYWVzKHggPSBZZWFyLCB5ID0gR0RQLCBjb2xvciA9IENvdW50cnkpICsgDQogIGdlb21fbGluZShzaXplID0gMSkNCg0KcDMNCmBgYA0KUmV2aXNhbmRvIGxhIMO6bHRpbWEgcG9ibGFjacOzbiByZWdpc3RyYWRhIGRlIGNhZGEgcGHDrXMuDQoNCmBgYHtyfQ0KZ2UgJT4lIA0KICBmaWx0ZXIoWWVhciA9PSAyMDE3KSAlPiUgDQogIGFycmFuZ2UoZGVzYyhQb3B1bGF0aW9uKSkNCmBgYA0KDQpWZW1vcyBxdWUgZXhpc3RlIHVuYSBncmFuIGRpZmVyZW5jaWEgZW4gbGEgcG9ibGFjacOzbiBkZSBlc3RvcyBwYcOtc2VzLiBQYXJhIHBvZGVyIGNvbXBhcmFyIGVsIFBJQiBlbnRyZSBlbGxvcywgc2Vyw61hIG1lam9yIHJlYWxpemFyIHVuICoqYWp1c3RlIHBvciBwb2JsYWNpw7NuKiogeSByZXZpc2FyIGxhIHZhcmlhYmxlICpQSUIgcGVyIGPDoXBpdGEqLg0KDQpgYGB7cn0NCmdlIDwtIGdsb2JhbF9lY29ub215ICU+JSANCiAgZmlsdGVyKENvdW50cnkgJWluJSBjKCJBdXN0cmFsaWEiLCAiTWV4aWNvIiwgIkljZWxhbmQiLCAiQ29sb21iaWEiKSkNCg0KcDQgPC0gZ2dwbG90KGdlKSArIGFlcyh4ID0gWWVhciwgeSA9IEdEUCAvIFBvcHVsYXRpb24sIGNvbG9yID0gQ291bnRyeSkgKw0KICBnZW9tX2xpbmUoc2l6ZSA9IDEpICsgeWxhYigiR0RQIHBlciBjYXBpdGEiKQ0KDQpwNA0KYGBgDQoNCiMjIFRyYW5zZm9ybWFjaW9uZXMgbWF0ZW3DoXRpY2FzDQoNCkNvbcO6bm1lbnRlIHNlIHBvZHLDoW4gbGxldmFyIGEgY2FibyB0cmFuc2Zvcm1hY2lvbmVzIGxvZ2Fyw610bWljYXMuDQoNCmBgYHtyfQ0KcDMgKyBzY2FsZV95X2xvZzEwKCkNCmBgYA0KDQoNCiMjIEFqdXN0ZXMgcG9yIGluZmxhY2nDs24NCg0KVG9tYXJlbW9zIGxhIHRhYmxhIHByZWNhcmdhZGEgYGF1c19yZXRhaWxgIHkgc2VsZWNjaW9uYXJlbW9zIGxhIGluZHVzdHJpYSBkZSBpbXByZXNpw7NuLiBSZXN1bWlyZW1vcyBsb3MgZGF0b3MgZGUgbWFuZXJhIGFudWFsIHkgY29tcGFyYXJlbW9zIGxhcyBncsOhZmljYXMgZGUgbGEgc2VyaWUgZW4gcHJlY2lvcyBjb3JyaWVudGVzIHkgYWp1c3RhZG9zIHBvciBpbmZsYWNpw7NuIChvIGEgKnByZWNpb3MgY29uc3RhbnRlcyopLg0KDQpgYGB7cn0NCnByaW50X3JldGFpbCA8LSBhdXNfcmV0YWlsICU+JQ0KICBmaWx0ZXIoSW5kdXN0cnkgPT0gIk5ld3NwYXBlciBhbmQgYm9vayByZXRhaWxpbmciKSAlPiUNCiAgZ3JvdXBfYnkoSW5kdXN0cnkpICU+JQ0KICBpbmRleF9ieShZZWFyID0geWVhcihNb250aCkpICU+JQ0KICBzdW1tYXJpc2UoVHVybm92ZXIgPSBzdW0oVHVybm92ZXIpKQ0KYXVzX2Vjb25vbXkgPC0gZ2xvYmFsX2Vjb25vbXkgJT4lDQogIGZpbHRlcihDb2RlID09ICJBVVMiKQ0KcHJpbnRfcmV0YWlsICU+JQ0KICBsZWZ0X2pvaW4oYXVzX2Vjb25vbXksIGJ5ID0gIlllYXIiKSAlPiUNCiAgbXV0YXRlKEFkanVzdGVkX3R1cm5vdmVyID0gVHVybm92ZXIgLyBDUEkpICU+JQ0KICBnYXRoZXIoIlR5cGUiLCAiVHVybm92ZXIiLCBUdXJub3ZlciwgQWRqdXN0ZWRfdHVybm92ZXIsIGZhY3Rvcl9rZXkgPSBUUlVFKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gWWVhciwgeSA9IFR1cm5vdmVyKSkgKw0KICAgIGdlb21fbGluZSgpICsNCiAgICBmYWNldF9ncmlkKHZhcnMoVHlwZSksIHNjYWxlcyA9ICJmcmVlX3kiKSArDQogICAgeGxhYigiWWVhcnMiKSArIHlsYWIoTlVMTCkgKw0KICAgIGdndGl0bGUoIlR1cm5vdmVyIGZvciB0aGUgQXVzdHJhbGlhbiBwcmludCBtZWRpYSBpbmR1c3RyeSIpDQpgYGANCg0KIyBFbCBlY29zaXN0ZW1hIHRpZHl2ZXIqdHMqDQoNCkVzdGFzIHBhcXVldGVyw61hcyBzb24gcGFydGUgZGVsIHRpZHl2ZXIqdHMqIHkgdXRpbGl6YW4gbGEgbWlzbWEgZmlsb3NvZsOtYSBkZWwgYHRpZHl2ZXJzZWAsIHBlcm8gc2UgZXNwZWNpYWxpemFuIGVuIGVsIGFuw6FsaXNpcyBkZSBzZXJpZXMgZGUgdGllbXBvLg0KDQpgYGB7cn0NCmxpYnJhcnkodHNpYmJsZSkNCmxpYnJhcnkoZmVhc3RzKQ0KbGlicmFyeShmYWJsZSkNCmBgYA0KDQojIyBUcmFuc2Zvcm1hY2lvbmVzIGRlIEJveC1Db3gNCg0KT3RyYSBmYW1pbGlhIGRlIHRyYW5zZm9ybWFjaW9uZXMsIHF1ZSBkZXBlbmRlIGRlIHVuIHBhcsOhbWV0cm8gJFxsYW1iZGEkIHBhcmEgZXNjb2dlciBlbCB0aXBvIGRlIHRyYW5zZm9ybWFjacOzbiBhIHJlYWxpemFyLiBQYXJhIGVqZW1wbGlmaWNhcmxvLCB0b21hbW9zIGxvcyBkYXRvcyBwcmVjYXJnYWRvcyBgYXVzX3Byb2R1Y3Rpb25gLg0KDQpgYGB7cn0NCmF1c19wcm9kdWN0aW9uDQpgYGANCg0KR3JhZmljYXIgbGEgcHJvZHVjY2nDs24gZGUgZ2FzLg0KDQpgYGB7cn0NCmF1c19wcm9kdWN0aW9uICU+JSBhdXRvcGxvdChHYXMpDQpgYGANCg0KUXVlcmVtb3MgKmVzdGFiaWxpemFyIGxhIHZhcmlhbnphKiB5IHVuYSB0cmFuc2Zvcm1hY2nDs24gbG9nYXLDrXRtaWNhIChvIEJveC1Db3ggY29uICRcbGFtYmRhID0gMCQpIHBhcmVjZSBubyBzZXIgc3VmaWNpZW50ZToNCg0KYGBge3J9DQphdXNfcHJvZHVjdGlvbiAlPiUgYXV0b3Bsb3QobG9nKEdhcykpDQpgYGANCg0KT2J0ZW5lbW9zIGxhICoqY2FyYWN0ZXLDrXN0aWNhIGRlIEd1ZXJyZXJvKiogcGFyYSBkZWNpZGlyIGVsIHZhbG9yIMOzcHRpbW8gZGUgJFxsYW1iZGEkLg0KDQpgYGB7cn0NCihsYW1iZGEgPC0gYXVzX3Byb2R1Y3Rpb24gJT4lDQogIGZlYXR1cmVzKEdhcywgZmVhdHVyZXMgPSBndWVycmVybykgJT4lDQogIHB1bGwobGFtYmRhX2d1ZXJyZXJvKSkNCmBgYA0KDQpQb2RlbW9zIGFwbGljYXIgbGEgdHJhbnNmb3JtYWNpw7NuIGRpcmVjdG8gZW4gZWwgYGF1dG9wbG90YC4NCg0KYGBge3J9DQphdXNfcHJvZHVjdGlvbiAlPiUgYXV0b3Bsb3QoYm94X2NveChHYXMsIGxhbWJkYSkpDQpgYGANCg0KIyMgRGVzY29tcG9zaWNpw7NuIGRlIHNlcmllcyBkZSAgdGllbXBvDQoNCmBgYHtyfQ0KdXNfcmV0YWlsX2VtcGxveW1lbnQgPC0gdXNfZW1wbG95bWVudCAlPiUNCiAgZmlsdGVyKHllYXIoTW9udGgpID49IDE5OTAsIFRpdGxlID09ICJSZXRhaWwgVHJhZGUiKSAlPiUNCiAgc2VsZWN0KC1TZXJpZXNfSUQpDQoNCnVzX3JldGFpbF9lbXBsb3ltZW50DQoNCnVzX3JldGFpbF9lbXBsb3ltZW50ICU+JQ0KICBhdXRvcGxvdChFbXBsb3llZCkgKw0KICB4bGFiKCJZZWFyIikgKyB5bGFiKCJQZXJzb25zICh0aG91c2FuZHMpIikgKw0KICBnZ3RpdGxlKCJUb3RhbCBlbXBsb3ltZW50IGluIFVTIHJldGFpbCIpDQpgYGANCg0KYGBge3J9DQpkY21wIDwtIHVzX3JldGFpbF9lbXBsb3ltZW50ICU+JQ0KICBtb2RlbChTVEwoRW1wbG95ZWQpKQ0KDQpjb21wb25lbnRzKGRjbXApDQpgYGANCg0KYGBge3J9DQp1c19yZXRhaWxfZW1wbG95bWVudCAlPiUNCiAgYXV0b3Bsb3QoRW1wbG95ZWQsIGNvbG9yPSdncmF5JykgKw0KICBhdXRvbGF5ZXIoY29tcG9uZW50cyhkY21wKSwgdHJlbmQsIGNvbG9yPSdyZWQnKSArDQogIHhsYWIoIlllYXIiKSArIHlsYWIoIlBlcnNvbnMgKHRob3VzYW5kcykiKSArDQogIGdndGl0bGUoIlRvdGFsIGVtcGxveW1lbnQgaW4gVVMgcmV0YWlsIikNCmBgYA0KDQpgYGB7cn0NCmNvbXBvbmVudHMoZGNtcCkgJT4lIGF1dG9wbG90KCkgKyB4bGFiKCJZZWFyIikNCmBgYA0KDQpgYGB7cn0NCnVzX3JldGFpbF9lbXBsb3ltZW50ICU+JQ0KICBhdXRvcGxvdChFbXBsb3llZCwgY29sb3I9J2dyYXknKSArDQogIGF1dG9sYXllcihjb21wb25lbnRzKGRjbXApLCBzZWFzb25fYWRqdXN0LCBjb2xvcj0nYmx1ZScpICsNCiAgeGxhYigiWWVhciIpICsgeWxhYigiUGVyc29ucyAodGhvdXNhbmRzKSIpICsNCiAgZ2d0aXRsZSgiVG90YWwgZW1wbG95bWVudCBpbiBVUyByZXRhaWwiKQ0KYGBgDQojIyMgRGVzY29tcG9zaWNpw7NuIGNsw6FzaWNhDQoNCmBgYHtyfQ0KdXNfcmV0YWlsX2VtcGxveW1lbnQgJT4lDQogIG1vZGVsKGNsYXNzaWNhbF9kZWNvbXBvc2l0aW9uKEVtcGxveWVkLCB0eXBlID0gImFkZGl0aXZlIikpICU+JQ0KICBjb21wb25lbnRzKCkgJT4lDQogIGF1dG9wbG90KCkgKyB4bGFiKCJZZWFyIikgKw0KICBnZ3RpdGxlKCJDbGFzc2ljYWwgYWRkaXRpdmUgZGVjb21wb3NpdGlvbiBvZiB0b3RhbCBVUyByZXRhaWwgZW1wbG95bWVudCIpDQpgYGANCg0KIyMjIERlc2NvbXBvc2ljacOzbiBYMTENCg0KYGBge3J9DQp4MTFfZGNtcCA8LSB1c19yZXRhaWxfZW1wbG95bWVudCAlPiUNCiAgbW9kZWwoeDExID0gZmVhc3RzOjo6WDExKEVtcGxveWVkLCB0eXBlID0gImFkZGl0aXZlIikpICU+JQ0KICBjb21wb25lbnRzKCkNCg0KYXV0b3Bsb3QoeDExX2RjbXApICsgeGxhYigiWWVhciIpICsNCiAgZ2d0aXRsZSgiQWRkaXRpdmUgWDExIGRlY29tcG9zaXRpb24gb2YgVVMgcmV0YWlsIGVtcGxveW1lbnQgaW4gdGhlIFVTIikNCmBgYA0KDQpgYGB7cn0NCngxMV9kY21wICU+JSANCiAgZ2dfc2Vhc29uKCkNCmBgYA0KDQpgYGB7cn0NCngxMV9kY21wICU+JSANCiAgZ2dfc3Vic2VyaWVzKHNlYXNvbmFsKQ0KYGBgDQojIyMgRGVzY29tcG9zaWNpw7NuIFNFQVRTDQoNCmBgYHtyfQ0Kc2VhdHNfZGNtcCA8LSB1c19yZXRhaWxfZW1wbG95bWVudCAlPiUNCiAgbW9kZWwoc2VhdHMgPSBmZWFzdHM6OjpTRUFUUyhFbXBsb3llZCkpICU+JQ0KICBjb21wb25lbnRzKCkNCmF1dG9wbG90KHNlYXRzX2RjbXApICsgeGxhYigiWWVhciIpICsNCiAgZ2d0aXRsZSgiU0VBVFMgZGVjb21wb3NpdGlvbiBvZiB0b3RhbCBVUyByZXRhaWwgZW1wbG95bWVudCIpDQpgYGANCg0KIyMjIERlc2NvbXBvc2ljacOzbiBTVEwNCg0KYGBge3J9DQp1c19yZXRhaWxfZW1wbG95bWVudCAlPiUNCiAgbW9kZWwoU1RMKEVtcGxveWVkIH4gdHJlbmQod2luZG93PTcpICsgc2Vhc29uKHdpbmRvdyA9ICJwZXJpb2RpYyIpLA0KICAgIHJvYnVzdCA9IFRSVUUpKSAlPiUNCiAgY29tcG9uZW50cygpICU+JQ0KICBhdXRvcGxvdCgpDQpgYGANCg0KYGBge3J9DQp1c2RfdHMgPC0gdXNkX2pweSAlPiUNCiAgYXNfdHNpYmJsZShpbmRleCA9IFRpbWVTdGFtcCkgJT4lIA0KICBmaWxsX2dhcHMoKSAlPiUgZmlsbChDbG9zZSkNCg0KdXNkX3RzICU+JSANCiAgbW9kZWwoU1RMKENsb3NlKSkgJT4lIA0KICBjb21wb25lbnRzKCkgJT4lIGF1dG9wbG90KCkNCmBgYA0KYGBge3J9DQpnZSAlPiUgDQogIG1vZGVsKFNUTChsb2coR0RQKSkpICU+JSANCiAgY29tcG9uZW50cygpICU+JSANCiAgYXV0b3Bsb3QoKQ0KYGBgDQoNCg0KDQo=